package com.lion.common.spring.openapi.customizers.openapi;

import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.PathItem;
import io.swagger.v3.oas.models.Paths;
import io.swagger.v3.oas.models.tags.Tag;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springdoc.core.customizers.OpenApiCustomizer;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import java.util.List;
import java.util.Map;

/**
 * @author Chris Chan
 * Create On 2025/10/2 上午11:20
 * Use for: 为OpenAPI文档中的tag添加前缀
 * Explain:
 * 有问题
 */
@Order(2000)
@Component
public class TagPrefixOpenApiCustomizer implements OpenApiCustomizer {
    private static final Logger log = LoggerFactory.getLogger(TagPrefixOpenApiCustomizer.class);

    @Override
    public void customise(OpenAPI openApi) {
        log.info("TagPrefixOpenApiCustomizer~~");
        // 先将tag名称映射到新名称
        Map<String, String> tagNameMap = new java.util.HashMap<>();
        // 相同tag名称的路径方法，会被合并到一个tag中
        MultiValueMap<String, Pair<String,PathItem>> pathMap = new LinkedMultiValueMap<>();
        // 给每个tag添加格式化后的前缀
        List<Tag> tags = addPrefixToTagName(openApi.getTags(), tagNameMap);
        // 更新OpenAPI文档中的tag列表
        openApi.setTags(tags);

        // 更新OpenAPI文档中的路径方法中的tag名称
        addPrefixToPathMethodTagName(openApi.getPaths(), tagNameMap, pathMap);
        // 按tag名称对路径方法进行排序，相同tag名称的路径方法，会被合并到一个tag中
        sortPathsBySameTag(pathMap);
        // 重新构建Paths对象
        openApi.setPaths(rebuildPaths(pathMap));
    }

    /**
     * 给每个tag添加格式化后的前缀，格式为：A001-标签名
     *
     * @param tags
     * @param tagNameMap
     * @return
     */
    private List<Tag> addPrefixToTagName(List<Tag> tags, Map<String, String> tagNameMap) {
        int size = tags.size();
        //log.info("Add tag prefix, tag size: {}", size);
        for (int i = 0; i < size; i++) {
            //获取size的位数
            int digits = Math.max((int) Math.floor(Math.log10(size)) + 1, 2);
            Tag tag = tags.get(i);
            String name = tag.getName();
            String newName = String.format("A%0" + digits + "d-%s", i + 1, name);
            tag.setName(newName);
            //log.info("Add tag prefix: {} -> {}", name, newName);

            tagNameMap.put(name, newName);
        }
        return tags;
    }

    /**
     * 给每个路径方法中的tag名称添加格式化后的前缀，格式为：A001-标签名
     *
     * @param paths
     * @param tagNameMap
     * @param pathMap
     */
    private void addPrefixToPathMethodTagName(Paths paths, Map<String, String> tagNameMap, MultiValueMap<String, Pair<String,PathItem>> pathMap) {
        paths.forEach((path, pathItem) -> {
            pathItem.readOperations().forEach(operation -> {
                modifyOperationTags(tagNameMap, operation, path,pathItem, pathMap);
            });
        });
    }

    /**
     * 为Operation添加排序
     *
     * @param tagNameMap
     * @param operation
     * @return
     */
    private void modifyOperationTags(Map<String, String> tagNameMap, Operation operation, String path, PathItem pathItem, MultiValueMap<String, Pair<String,PathItem>> pathMap) {
        if (operation != null) {
            for (String tag : operation.getTags()) {
                operation.getTags().remove(tag);
                String tagNewName = tagNameMap.get(tag);
                if (tagNewName != null) {
                    operation.getTags().add(tagNewName);
                    // 相同tag名称的路径方法，会被合并到一个tag中
                    pathMap.add(tagNewName, Pair.of(path, pathItem));
                }
            }
        }
    }

    private void sortPathsBySameTag(MultiValueMap<String, Pair<String,PathItem>> pathMap) {
        pathMap.forEach((tag, pathItems) -> {
            pathItems.sort((o1, o2) -> {
                Integer o1Order = findMinOrder(o1.getRight());
                Integer o2Order = findMinOrder(o2.getRight());
                return Integer.compare(o1Order, o2Order);
            });
        });
    }

    /**
     * 找出PathItem所有Operation中order值最小的
     *
     * @param pathItem
     * @return
     */
    private Integer findMinOrder(PathItem pathItem) {
        Integer minOrder = Integer.MAX_VALUE;
        for (Operation operation : pathItem.readOperations()) {
            Integer order = (Integer) operation.getExtensions().get("x-path-order");
            if (order != null && order < minOrder) {
                minOrder = order;
            }
        }
        return minOrder;
    }

    // 重整Paths，重新构建一个Paths，将pathMap中的路径方法，按照tag名称排序后，添加到新的Paths中
    private Paths rebuildPaths(MultiValueMap<String, Pair<String,PathItem>> pathMap) {
        Paths paths = new Paths();
        pathMap.forEach((tag, pathItems) -> {
            pathItems.forEach(pathItem -> {
                paths.addPathItem(pathItem.getLeft(), pathItem.getRight());
            });
        });
        return paths;
    }
}
